package com.springcloud.sc.ucpv5.client.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.apache.commons.lang3.StringUtils;


public class ZipUtil {

	/**
	 * 递归压缩文件夹
	 * 
	 * @param srcRootDir
	 *            压缩文件夹根目录的子路径
	 * @param file
	 *            当前递归压缩的文件或目录对象
	 * @param zos
	 *            压缩文件存储对象
	 * @throws Exception
	 */
	private static void zip(String srcRootDir, File file, ZipOutputStream zos) throws Exception {
		if (file == null) {
			return;
		}

		// 如果是文件，则直接压缩该文件
		if (file.isFile()) {
			int count, bufferLen = 1024;
			byte data[] = new byte[bufferLen];

			// 获取文件相对于压缩文件夹根目录的子路径
			String subPath = file.getAbsolutePath();
			int index = subPath.indexOf(srcRootDir);
			if (index != -1) {
				subPath = subPath.substring(srcRootDir.length() + File.separator.length());
			}
			ZipEntry entry = new ZipEntry(subPath);
			zos.putNextEntry(entry);
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			while ((count = bis.read(data, 0, bufferLen)) != -1) {
				zos.write(data, 0, count);
			}
			bis.close();
			zos.closeEntry();

			System.out.println(file + "压缩完成");
		}
		// 如果是目录，则压缩整个目录
		else {
			// 压缩目录中的文件或子目录
			File[] childFileList = file.listFiles();
			for (int n = 0; n < childFileList.length; n++) {
				childFileList[n].getAbsolutePath().indexOf(file.getAbsolutePath());
				zip(srcRootDir, childFileList[n], zos);
			}
		}
	}

	/**
	 * 对文件或文件目录进行压缩
	 * 
	 * @param srcPath
	 *            要压缩的源文件路径。如果压缩一个文件，则为该文件的全路径；如果压缩一个目录，则为该目录的顶层目录路径
	 * @param zipPath
	 *            压缩文件保存的路径。注意：zipPath不能是srcPath路径下的子文件夹
	 * @param zipFileName
	 *            压缩文件名
	 * @throws Exception
	 */
	public static String zip(String srcPath, String zipPath, String zipFileName) throws Exception {
		if (StringUtils.isEmpty(srcPath) || StringUtils.isEmpty(zipPath) || StringUtils.isEmpty(zipFileName)) {
			return "无文件指定";
		}
		CheckedOutputStream cos = null;
		ZipOutputStream zos = null;
		try {
			File srcFile = new File(srcPath);

			// 判断压缩文件保存的路径是否为源文件路径的子文件夹，如果是，则抛出异常（防止无限递归压缩的发生）
			if (srcFile.isDirectory() && zipPath.indexOf(srcPath) != -1) {
				return "zipPath must not be the child directory of srcPath.";
			}

			// 判断压缩文件保存的路径是否存在，如果不存在，则创建目录
			File zipDir = new File(zipPath);
			if (!zipDir.exists() || !zipDir.isDirectory()) {
				zipDir.mkdirs();
			}

			// 创建压缩文件保存的文件对象
			String zipFilePath = zipPath + File.separator + zipFileName;
			File zipFile = new File(zipFilePath);
			if (zipFile.exists()) {
				// 检测文件是否允许删除，如果不允许删除，将会抛出SecurityException
				SecurityManager securityManager = new SecurityManager();
				securityManager.checkDelete(zipFilePath);
				// 删除已存在的目标文件
				zipFile.delete();
			}

			cos = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
			zos = new ZipOutputStream(cos);

			// 如果只是压缩一个文件，则需要截取该文件的父目录
			String srcRootDir = srcPath;
			if (srcFile.isFile()) {
				int index = srcPath.lastIndexOf(File.separator);
				if (index != -1) {
					srcRootDir = srcPath.substring(0, index);
				}
			}
			// 调用递归压缩方法进行目录或文件压缩
			zip(srcRootDir, srcFile, zos);
			zos.flush();
		} catch (Exception e) {
			return e.getMessage();
		} finally {
			try {
				if (zos != null) {
					zos.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return null;
	}

	public static String unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName) {
		return unzip(zipFilePath, unzipFilePath, includeZipFileName, "GBK");
	}

	/**
	 * 解压缩zip包
	 * 
	 * @param zipFilePath
	 *            zip文件的全路径
	 * @param unzipFilePath
	 *            解压后的文件保存的路径
	 * @param includeZipFileName
	 *            解压后的文件保存的路径是否包含压缩文件的文件名。true-包含；false-不包含
	 */
	@SuppressWarnings("unchecked")
	public static String unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName, String charset) {
		try {
			File zipFile = new File(zipFilePath);
			if (StringUtils.isEmpty(zipFilePath) || StringUtils.isEmpty(unzipFilePath)) {
				return "无文件指定";
			}
			// 如果解压后的文件保存路径包含压缩文件的文件名，则追加该文件名到解压路径
			if (includeZipFileName) {
				String fileName = zipFile.getName();
				if (StringUtils.isNotEmpty(fileName)) {
					fileName = fileName.substring(0, fileName.lastIndexOf("."));
				}
				unzipFilePath = unzipFilePath + File.separator + fileName;
			}
			// 创建解压缩文件保存的路径
			File unzipFileDir = new File(unzipFilePath);
			if (unzipFileDir.exists()) {
				// 删除
				FileUtil.deleteAllFilesOfDir(unzipFileDir);
				System.out.println("删除原有目录成功.");
				// 防止源文件刚删除就写, 出现不可读异常
				Thread.sleep(1000);
			}

			// 构建新的目录
			unzipFileDir.mkdirs();

			// 开始解压
			ZipEntry entry = null;
			String entryFilePath = null, entryDirPath = null;
			File entryFile = null, entryDir = null;
			int index = 0, count = 0, bufferSize = 1024;
			byte[] buffer = new byte[bufferSize];
			BufferedInputStream bis = null;
			BufferedOutputStream bos = null;
			ZipFile zip = new ZipFile(zipFile, Charset.forName("GBK"));
			Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
			// 循环对压缩包里的每一个文件进行解压
			while (entries.hasMoreElements()) {
				try {
					entry = entries.nextElement();
					// 构建压缩包中一个文件解压后保存的文件全路径
					entryFilePath = unzipFilePath + File.separator + entry.getName();
					// 构建解压后保存的文件夹路径
					index = entryFilePath.lastIndexOf(File.separator);
					if (index != -1) {
						entryDirPath = entryFilePath.substring(0, index);
					} else {
						entryDirPath = "";
					}
					entryDir = new File(entryDirPath);
					// 如果文件夹路径不存在，则创建文件夹
					if (!entryDir.exists() || !entryDir.isDirectory()) {
						entryDir.mkdirs();
					}
					// 创建解压文件
					entryFile = new File(entryFilePath);
					if (entryFile.exists()) {
						if (entry.isDirectory()) {
							continue;
						} else {
							// 检测文件是否允许删除，如果不允许删除，将会抛出SecurityException
							SecurityManager securityManager = new SecurityManager();
							securityManager.checkDelete(entryFilePath);
							// 删除已存在的目标文件
							entryFile.delete();
						}
						(new File(entryFile.getParent())).mkdirs();
					} else {
						if (entry.isDirectory()) {
							entryFile.mkdir();
							continue;
						}
					}

					// 写入文件
					bos = new BufferedOutputStream(new FileOutputStream(entryFile));
					bis = new BufferedInputStream(zip.getInputStream(entry));
					while ((count = bis.read(buffer, 0, bufferSize)) != -1) {
						bos.write(buffer, 0, count);
					}
					bos.flush();
					bos.close();
					System.out.println(entryFile + "解压完成");
				} catch (Exception e) {
					// TODO: handle exception
				} finally {
					try {
						bos.flush();
						bos.close();
					} catch (Exception e2) {
						// TODO: handle exception
					}
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}

		return null;
	}

	/**
	 * 解压rar格式压缩包。
	 * 对应的是java-unrar-0.3.jar，但是java-unrar-0.3.jar又会用到commons-logging-1.1.1.jar
	 */
	/*private static String unrar(String sourceRar, String destDir) {
		destDir = destDir + File.separator;

		Archive a = null;
		FileOutputStream fos = null;
		try {
			a = new Archive(new File(sourceRar));
			FileHeader fh = a.nextFileHeader();
			while (fh != null) {
				if (!fh.isDirectory()) {
					// 1 根据不同的操作系统拿到相应的 destDirName 和 destFileName
					String compressFileName = fh.getFileNameString().trim();
					String destFileName = "";
					String destDirName = "";
					// 非windows系统
					if (File.separator.equals("/")) {
						destFileName = destDir + compressFileName.replaceAll("\\\\", "/");
						destDirName = destFileName.substring(0, destFileName.lastIndexOf("/"));
						// windows系统
					} else {
						destFileName = destDir + compressFileName.replaceAll("/", "\\\\");
						destDirName = destFileName.substring(0, destFileName.lastIndexOf("\\"));
					}
					// 2创建文件夹
					File dir = new File(destDirName);
					if (!dir.exists() || !dir.isDirectory()) {
						dir.mkdirs();
					}
					// 3解压缩文件
					fos = new FileOutputStream(new File(destFileName));
					a.extractFile(fh, fos);
					fos.close();
					fos = null;
					System.out.println(destFileName + "解压完成");
				}
				fh = a.nextFileHeader();
			}
			a.close();
			a = null;
		} catch (Exception e) {
			return e.getMessage();
		} finally {
			if (fos != null) {
				try {
					fos.close();
					fos = null;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if (a != null) {
				try {
					a.close();
					a = null;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		return null;
	}*/
	
/*	public static void main(String[] args) {
		String dir = "c:\\tmp\\version_data";
		String zipPath = "c:\\tmp";
		String zipFileName = "version_data1.zip";
		try {
			// zip(dir, zipPath, zipFileName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		String zipFilePath = "c:\\tmp\\shiro_demo.rar";
		String unzipFilePath = "c:\\tmp";
	}*/
}
